package com.xli.ui.module.framemodule.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.CacheManager;
import com.alicp.jetcache.anno.CacheConsts;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.template.QuickConfig;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xli.dto.component.util.TreeUtil;
import com.xli.organ.role.framerole.entity.FrameRole;
import com.xli.organ.role.framerole.service.IFrameRoleService;
import com.xli.organ.user.frameuser.entity.FrameUser;
import com.xli.organ.user.frameuser.service.IFrameUserService;
import com.xli.organ.user.frameuserrolerelation.entity.FrameUserRoleRelation;
import com.xli.organ.user.frameuserrolerelation.service.IFrameUserRoleRelationService;
import com.xli.ui.module.framemodule.entity.FrameModule;
import com.xli.ui.module.framemodule.entity.ModuleCache;
import com.xli.ui.module.framemodule.mapper.FrameModuleMapper;
import com.xli.ui.module.framemodule.mapper.IModuleCacheMapper;
import com.xli.ui.module.framemodule.service.IFrameModuleService;
import com.xli.ui.module.framemodulerole.entity.FrameModuleRole;
import com.xli.ui.module.framemodulerole.service.IFrameModuleRoleService;
import com.xli.ui.module.framemoduleuser.entity.FrameModuleUser;
import com.xli.ui.module.framemoduleuser.service.IFrameModuleUserService;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Validated
@Service
public class FrameModuleServiceImpl extends ServiceImpl<FrameModuleMapper, FrameModule> implements IFrameModuleService {

    private final Lock lock = new ReentrantLock();
    @Autowired
    IFrameUserService frameUserService;
    @Autowired
    IFrameRoleService iFrameRoleService;
    @Autowired
    IFrameUserRoleRelationService iFrameUserRoleRelationService;
    @Autowired
    IFrameModuleRoleService iFrameModuleRoleService;
    @Autowired
    IFrameModuleUserService iFrameModuleUserService;
    @Autowired
    CacheManager cacheManager;
    Cache<Long, List<ModuleCache>> cache;

    @PostConstruct
    public void init() {
        lock.lock();
        try {
            QuickConfig qc = QuickConfig.newBuilder("frame_module:list")
                    .expire(Duration.ofSeconds(CacheConsts.DEFAULT_EXPIRE))
                    .cacheType(CacheType.BOTH)
                    .syncLocal(true)
                    .build();
            cache = cacheManager.getOrCreateCache(qc);

            List<FrameModule> frameModuleList = this.findList(new QueryWrapper<>());
            List<ModuleCache> cacheList = IModuleCacheMapper.INSTANCE.toCacheList(frameModuleList);

            Map<Long, List<ModuleCache>> groupList = cacheList.stream()
                    .collect(Collectors.groupingBy(module -> Optional.ofNullable(module.getPid()).orElse(TreeUtil.defaultPid)));
            groupList.forEach((pid, moduleList) -> {
                moduleList.sort(Comparator.comparing(ModuleCache::getOrder_num, Comparator.nullsLast(Comparator.reverseOrder())));
                cache.put(pid, moduleList);
            });
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean insert(FrameModule frameModule) {
        lock.lock();
        try {
            boolean result = this.save(frameModule);
            if (result) {
                List<ModuleCache> cacheList = cache.get(generateKey(frameModule));
                if (cacheList == null) {
                    cacheList = new ArrayList<>();
                }
                cacheList.add(IModuleCacheMapper.INSTANCE.toCacheEntity(frameModule));
                cacheList.sort(Comparator.comparing(ModuleCache::getOrder_num, Comparator.nullsLast(Comparator.reverseOrder())));
                cache.put(generateKey(frameModule), cacheList);
            }
            return result;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean delete(Long id) {
        Queue<Long> queue = new LinkedList<>();
        queue.offer(id);

        while (!queue.isEmpty()) {
            Long currentId = queue.poll();
            FrameModule frameModule = this.find(currentId);
            if (frameModule != null) {
                lock.lock();
                try {
                    boolean result = this.removeById(frameModule.getId());
                    if (result) {
                        // 更新缓存
                        List<ModuleCache> cacheList = cache.get(generateKey(frameModule));
                        if (cacheList != null) {
                            cacheList.removeIf(module -> module.getId().equals(frameModule.getId()));
                            cache.put(generateKey(frameModule), cacheList);
                        }
                    }
                } finally {
                    lock.unlock();
                }

                // 删除关联的权限
                List<FrameModuleRole> frameModuleRoleList = iFrameModuleRoleService.findModuleRoleListByModuleId(frameModule.getId());
                for (FrameModuleRole frameModuleRole : frameModuleRoleList) {
                    iFrameModuleRoleService.delete(frameModuleRole.getId());
                }
                List<FrameModuleUser> frameModuleUserList = iFrameModuleUserService.findModuleUserListByModuleId(frameModule.getId());
                for (FrameModuleUser frameModuleUser : frameModuleUserList) {
                    iFrameModuleUserService.delete(frameModuleUser.getId());
                }

                // 查找子项并加入队列
                List<ModuleCache> subCacheList = new ArrayList<>();
                findModuleCacheList(frameModule.getId(), subCacheList);
                for (ModuleCache moduleCache : subCacheList) {
                    queue.offer(moduleCache.getId());
                }
            }
        }
        return true;
    }

    @Override
    public boolean update(FrameModule frameModule) {
        lock.lock();
        try {
            FrameModule module = this.find(frameModule.getId());
            if (module != null) {
                boolean result = this.updateById(frameModule);
                if (result) {
                    List<ModuleCache> cacheList = cache.get(generateKey(module));
                    if (cacheList == null) {
                        cacheList = new ArrayList<>();
                    }
                    cacheList.removeIf(cache -> cache.getId().equals(module.getId()));

                    if (module.getPid().equals(frameModule.getPid())) {
                        cacheList.add(IModuleCacheMapper.INSTANCE.toCacheEntity(frameModule));
                        cacheList.sort(Comparator.comparing(ModuleCache::getOrder_num, Comparator.nullsLast(Comparator.reverseOrder())));
                        cache.put(generateKey(module), cacheList);
                    } else {
                        cache.put(generateKey(module), cacheList);

                        cacheList = cache.get(generateKey(frameModule));
                        if (cacheList == null) {
                            cacheList = new ArrayList<>();
                        }
                        cacheList.add(IModuleCacheMapper.INSTANCE.toCacheEntity(frameModule));
                        cacheList.sort(Comparator.comparing(ModuleCache::getOrder_num, Comparator.nullsLast(Comparator.reverseOrder())));
                        cache.put(generateKey(frameModule), cacheList);
                    }
                }
                return result;
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public FrameModule find(Long id) {
        return this.getById(id);
    }

    @Override
    public long findCount(QueryWrapper<FrameModule> qw) {
        return this.count(qw);
    }

    @Override
    public List<FrameModule> findList(QueryWrapper<FrameModule> qw) {
        return this.list(qw);
    }

    @Override
    public Page<FrameModule> findList(QueryWrapper<FrameModule> qw, long current, long size) {
        return this.page(new Page<>(current, size), qw);
    }

    @Override
    public List<FrameModule> findModuleListById(Long id) {
        List<FrameModule> frameModuleList = new ArrayList<>();
        FrameModule frameModule = this.find(id);
        if (frameModule != null) {
            frameModuleList.add(frameModule);
            frameModuleList.addAll(findModuleListByPid(frameModule.getId()));
            return frameModuleList;
        }
        return new ArrayList<>();
    }

    @Override
    public Page<FrameModule> findModuleListById(Long id, long current, long size) {
        List<ModuleCache> cacheList = new ArrayList<>();
        FrameModule frameModule = this.find(id);
        if (frameModule != null) {
            cacheList.add(IModuleCacheMapper.INSTANCE.toCacheEntity(frameModule));

            findModuleCacheList(frameModule.getId(), cacheList);
            List<ModuleCache> moduleCacheList = CollUtil.page((int) (current - 1), (int) size, cacheList);
            return new Page<FrameModule>(current, size, cacheList.size()).setRecords(IModuleCacheMapper.INSTANCE.toEntityList(moduleCacheList, this::find));
        }

        List<FrameModule> frameModuleList = findModuleListById(id);
        return new Page<FrameModule>(current, size, frameModuleList.size()).setRecords(CollUtil.page((int) (current - 1), (int) size, frameModuleList));
    }

    @Override
    public List<FrameModule> findModuleListByPid(Long pid) {
        List<ModuleCache> cacheList = new ArrayList<>();
        findModuleCacheList(pid, cacheList);
        return IModuleCacheMapper.INSTANCE.toEntityList(cacheList, this::find);
    }

    @Override
    public Page<FrameModule> findModuleListByPid(Long pid, long current, long size) {
        List<ModuleCache> cacheList = new ArrayList<>();
        findModuleCacheList(pid, cacheList);
        List<ModuleCache> moduleCacheList = CollUtil.page((int) (current - 1), (int) size, cacheList);
        return new Page<FrameModule>(current, size, cacheList.size()).setRecords(IModuleCacheMapper.INSTANCE.toEntityList(moduleCacheList, this::find));
    }

    @Override
    public Page<FrameModule> findModuleListByPid(Long pid, String type, String name, long current, long size) {
        List<ModuleCache> cacheList = new ArrayList<>();
        findModuleCacheList(pid, cacheList);
        if (!StrUtil.isEmpty(type)) {
            Set<String> typeSet = new HashSet<>();
            if (type.contains(",")) {
                String[] typeArr = type.split(",");
                typeSet.addAll(Arrays.asList(typeArr));
            }
            else {
                typeSet.add(type);
            }
            cacheList = cacheList.stream()
                    .filter(module -> typeSet.contains(module.getType()))
                    .collect(Collectors.toList());
        }
        if (!StrUtil.isEmpty(name)) {
            cacheList = cacheList.stream()
                    .filter(module -> module.getName().contains(name))
                    .collect(Collectors.toList());
        }
        List<ModuleCache> moduleCacheList = CollUtil.page((int) (current - 1), (int) size, cacheList);
        return new Page<FrameModule>(current, size, cacheList.size()).setRecords(IModuleCacheMapper.INSTANCE.toEntityList(moduleCacheList, this::find));
    }

    @Override
    public List<FrameModule> findModuleListByUserId(Long userId) {
        List<FrameModule> moduleList = new ArrayList<>();
        Set<Long> moduleIdList = findUserModuleList(userId);
        for (Long moduleId : moduleIdList) {
            FrameModule module = this.find(moduleId);
            if (module != null) {
                moduleList.add(module);
            }
        }
        return moduleList;
    }

    @Override
    public boolean checkModuleRight(Long userId, Long moduleId) {
        Set<Long> moduleIdList = findUserModuleList(userId);
        return moduleIdList.contains(moduleId);
    }

    private Set<Long> findUserModuleList(Long userId) {
        Set<Long> moduleIdList = new HashSet<>();
        FrameUser frameUser = frameUserService.find(userId);
        if (frameUser != null) {
            //查询当前用户的所有角色
            List<FrameUserRoleRelation> frameUserRoleRelationList = iFrameUserRoleRelationService.findRoleListByUserId(frameUser.getId());
            for (FrameUserRoleRelation frameUserRoleRelation : frameUserRoleRelationList) {
                FrameRole frameRole = iFrameRoleService.find(frameUserRoleRelation.getRole_id());
                if (frameRole != null) {
                    //添加到当前用户角色集合中
                    List<FrameModuleRole> frameRoleModuleList = iFrameModuleRoleService.findModuleRoleListByRoleId(frameRole.getId());
                    for (FrameModuleRole frameRoleModule : frameRoleModuleList) {
                        moduleIdList.add(frameRoleModule.getModule_id());
                    }
                }
            }
            //查询角色用户权限
            List<FrameModuleUser> frameUserModuleList = iFrameModuleUserService.findModuleUserListByUserId(frameUser.getId());
            for (FrameModuleUser frameUserModule : frameUserModuleList) {
                moduleIdList.add(frameUserModule.getModule_id());
            }
        }
        return moduleIdList;
    }

    private void findModuleCacheList(Long pid, List<ModuleCache> moduleCacheList) {
        if (pid == null) {
            pid = TreeUtil.defaultPid;
        }
        // 使用队列实现广度优先遍历
        Queue<Long> queue = new LinkedList<>();
        queue.offer(pid); // 将初始 pid 加入队列

        while (!queue.isEmpty()) {
            Long currentPid = queue.poll(); // 从队列中取出当前 pid
            // 获取当前 pid 对应的缓存列表
            List<ModuleCache> cacheList = cache.get(currentPid);
            if (cacheList == null) {
                cacheList = new ArrayList<>();
            }
            // 将当前缓存列表添加到结果中
            moduleCacheList.addAll(cacheList);
            // 将当前缓存列表中的模块 ID 加入队列，用于下一层遍历
            for (ModuleCache moduleCache : cacheList) {
                queue.offer(moduleCache.getId());
            }
        }
    }

    private Long generateKey(FrameModule frameModule) {
        if (frameModule != null && frameModule.getPid() != null) {
            return frameModule.getPid();
        }
        return TreeUtil.defaultPid;
    }
}
